/*
    Copyright (c) 2008 Volker Krause <vkrause@kde.org>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "agentinstancecreatejob.h"

#include "agentinstance.h"
#include "agentmanager.h"
#include "agentmanager_p.h"
#include "controlinterface.h"
#include "dbusconnectionpool.h"
#include "kjobprivatebase_p.h"
#include "servermanager.h"

#include <kdebug.h>
#include <klocalizedstring.h>

#include <QtCore/QTimer>

#ifdef Q_OS_UNIX
#include <sys/types.h>
#include <signal.h>
#endif

using namespace Akonadi;

#if defined(Q_WS_MAEMO_5) || defined(Q_OS_WINCE)
static const int safetyTimeout = 60000; // ms
#else
static const int safetyTimeout = 10000; // ms
#endif


namespace Akonadi {
/**
 * @internal
 */
class AgentInstanceCreateJobPrivate : public KJobPrivateBase
{
  public:
    AgentInstanceCreateJobPrivate( AgentInstanceCreateJob* parent ) : q( parent ),
      parentWidget( 0 ),
      safetyTimer( new QTimer( parent ) ),
      doConfig( false ),
      tooLate( false )
    {
      QObject::connect( AgentManager::self(), SIGNAL(instanceAdded(Akonadi::AgentInstance)),
                        q, SLOT(agentInstanceAdded(Akonadi::AgentInstance)) );
      QObject::connect( safetyTimer, SIGNAL(timeout()), q, SLOT(timeout()) );
    }

    void agentInstanceAdded( const AgentInstance &instance )
    {
      if ( agentInstance == instance && !tooLate ) {
        safetyTimer->stop();
        if ( doConfig ) {
          // return from dbus call first before doing the next one
          QTimer::singleShot( 0, q, SLOT(doConfigure()) );
        } else {
          q->emitResult();
        }
      }
    }

    void doConfigure()
    {
      org::freedesktop::Akonadi::Agent::Control *agentControlIface =
        new org::freedesktop::Akonadi::Agent::Control( ServerManager::agentServiceName( ServerManager::Agent, agentInstance.identifier() ),
                                                       QLatin1String( "/" ), DBusConnectionPool::threadConnection(), q );
      if ( !agentControlIface || !agentControlIface->isValid() ) {
        delete agentControlIface;

        q->setError( KJob::UserDefinedError );
        q->setErrorText( i18n( "Unable to access D-Bus interface of created agent." ) );
        q->emitResult();
        return;
      }

      q->connect( agentControlIface, SIGNAL(configurationDialogAccepted()),
                  q, SLOT(configurationDialogAccepted()) );
      q->connect( agentControlIface, SIGNAL(configurationDialogRejected()),
                  q, SLOT(configurationDialogRejected()) );

      agentInstance.configure( parentWidget );
    }

    void configurationDialogAccepted()
    {
      // The user clicked 'Ok' in the initial configuration dialog, so we assume
      // he wants to keep the resource and the job is done.
      q->emitResult();
    }

    void configurationDialogRejected()
    {
      // The user clicked 'Cancel' in the initial configuration dialog, so we assume
      // he wants to abort the 'create new resource' job and the new resource will be
      // removed again.
      AgentManager::self()->removeInstance( agentInstance );

      q->emitResult();
    }

    void timeout()
    {
      tooLate = true;
      q->setError( KJob::UserDefinedError );
      q->setErrorText( i18n( "Agent instance creation timed out." ) );
      q->emitResult();
    }

    void emitResult()
    {
      q->emitResult();
    }

    void doStart();

    AgentInstanceCreateJob* q;
    AgentType agentType;
    QString agentTypeId;
    AgentInstance agentInstance;
    QWidget* parentWidget;
    QTimer *safetyTimer;
    bool doConfig;
    bool tooLate;
};

}

AgentInstanceCreateJob::AgentInstanceCreateJob( const AgentType &agentType, QObject *parent )
  : KJob( parent ),
    d( new AgentInstanceCreateJobPrivate( this ) )
{
  d->agentType = agentType;
}

AgentInstanceCreateJob::AgentInstanceCreateJob( const QString &typeId, QObject *parent )
  : KJob( parent ),
    d( new AgentInstanceCreateJobPrivate( this ) )
{
  d->agentTypeId = typeId;
}

AgentInstanceCreateJob::~ AgentInstanceCreateJob()
{
  delete d;
}

void AgentInstanceCreateJob::configure( QWidget *parent )
{
  d->parentWidget = parent;
  d->doConfig = true;
}

AgentInstance AgentInstanceCreateJob::instance() const
{
  return d->agentInstance;
}

void AgentInstanceCreateJob::start()
{
  d->start();
}

void AgentInstanceCreateJobPrivate::doStart()
{
  if ( !agentType.isValid() && !agentTypeId.isEmpty() ) {
    agentType = AgentManager::self()->type( agentTypeId );
  }

  if ( !agentType.isValid() ) {
    q->setError( KJob::UserDefinedError );
    q->setErrorText( i18n( "Unable to obtain agent type '%1'.", agentTypeId ) );
    QTimer::singleShot( 0, q, SLOT(emitResult()) );
    return;
  }

  agentInstance = AgentManager::self()->d->createInstance( agentType );
  if ( !agentInstance.isValid() ) {
    q->setError( KJob::UserDefinedError );
    q->setErrorText( i18n( "Unable to create agent instance." ) );
    QTimer::singleShot( 0, q, SLOT(emitResult()) );
  } else {
    int timeout = safetyTimeout;
#ifdef Q_OS_UNIX
    // Increate the timeout when valgrinding the agent, because that slows down things a log.
    QString agentValgrind = QString::fromLocal8Bit( qgetenv( "AKONADI_VALGRIND" ) );
    if ( !agentValgrind.isEmpty() && agentType.identifier().contains( agentValgrind ) ) {
      timeout *= 15;
    }

    // change the timeout when debugging the agent, because we need time to start the debugger
    const QString agentDebugging = QString::fromLocal8Bit( qgetenv( "AKONADI_DEBUG_WAIT" ) );
    if ( !agentDebugging.isEmpty() ) {
      // we are debugging
      const QString agentDebuggingTimeout = QString::fromLocal8Bit( qgetenv( "AKONADI_DEBUG_TIMEOUT" ) );
      if ( agentDebuggingTimeout.isEmpty() ) {
        // use default value of 150 seconds (the same as "valgrinding", this has to be checked)
        timeout = 15* safetyTimeout;
      } else {
        // use own value
        timeout = agentDebuggingTimeout.toInt();
      }
    }
#endif
    safetyTimer->start( timeout );
  }
}

#include "moc_agentinstancecreatejob.cpp"
